'use client';

import { useState, useEffect, useCallback } from 'react';
import { ethers } from 'ethers';
import { toast } from 'react-hot-toast';
import type { CreateXLayerTokenParams, XLayerTradeParams } from '@/types/xlayer';
import { apiClient } from '@/lib/api';

// X Layer network configuration - Terigon testnet
const XLAYER_NETWORK = {
  chainId: parseInt(process.env.NEXT_PUBLIC_XLAYER_CHAIN_ID || '1952'), // X Layer Terigon Testnet
  chainName: 'X Layer Terigon Testnet',
  nativeCurrency: {
    name: 'OKB',
    symbol: 'OKB',
    decimals: 18,
  },
  rpcUrls: [process.env.NEXT_PUBLIC_XLAYER_RPC_URL!],
  blockExplorerUrls: ['https://www.oklink.com/xlayer-test'],
};

// Contract addresses and ABI (aligned with XLayerLaunchPad.sol)
const LAUNCHPAD_ADDRESS = process.env.NEXT_PUBLIC_XLAYER_LAUNCHPAD_ADDRESS as string;
if (!LAUNCHPAD_ADDRESS) {
  throw new Error('NEXT_PUBLIC_XLAYER_LAUNCHPAD_ADDRESS environment variable is required');
}
const LAUNCHPAD_ABI = [
  // Core state
  'function nextLaunchId() view returns (uint256)',
  'function platformFeePercent() view returns (uint256)',
  'function MIN_TARGET() view returns (uint256)',
  'function MAX_TARGET() view returns (uint256)',

  // Launch lifecycle
  'function createTokenLaunch(string,string,string,string,string,string,uint256) payable',
  'function buyTokens(uint256) payable',
  'function sellTokens(uint256,uint256)',
  'function manualGraduateToken(uint256)',
  'function cancelLaunch(uint256)',
  'function claimRefund(uint256)',

  // Views
  'function getLaunchInfo(uint256) view returns (address tokenAddress, address creator, string name, string symbol, string description, uint256 targetAmount, uint256 raisedAmount, uint256 deadline, bool launched, bool cancelled, uint256 currentPrice, uint256 tokensSold, uint256 totalSupplyForSale)',
  'function getUserContribution(uint256,address) view returns (uint256)',
  'function getUserTokenBalance(uint256,address) view returns (uint256)',
  'function getCurrentTokenPrice(uint256) view returns (uint256)',

  // Events
  'event TokenLaunchCreated(uint256 indexed launchId, address indexed creator, address tokenAddress, string name, string symbol, uint256 targetAmount)',
  'event TokenPurchase(uint256 indexed launchId, address indexed buyer, uint256 ethAmount, uint256 tokensReceived, uint256 newPrice)',
  'event TokenSale(uint256 indexed launchId, address indexed seller, uint256 tokensAmount, uint256 ethReceived, uint256 newPrice)',
  'event TokenGraduated(uint256 indexed launchId, address indexed tokenAddress, address pairAddress, uint256 liquidityETH, uint256 liquidityTokens)',
  'event RefundClaimed(uint256 indexed launchId, address indexed contributor, uint256 amount)'
];

const ERC20_ABI = [
  'function balanceOf(address owner) view returns (uint256)',
  'function symbol() view returns (string)',
  'function name() view returns (string)',
  'function decimals() view returns (uint8)',
];

declare global {
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
  interface Window { ethereum?: any }
}

export function useXLayerWallet() {
  const [isConnected, setIsConnected] = useState(false);
  const [address, setAddress] = useState<string | null>(null);
  const [balance, setBalance] = useState<string>('0');
  const [provider, setProvider] = useState<ethers.BrowserProvider | null>(null);
  const [signer, setSigner] = useState<ethers.JsonRpcSigner | null>(null);
  const [isLoading, setIsLoading] = useState(false);

  // Check if wallet is connected on mount
  useEffect(() => {
      checkConnection();
      // eslint-disable-next-line react-hooks/exhaustive-deps
    }, []);

  const checkConnection = async () => {
    if (typeof window !== 'undefined' && window.ethereum) {
      try {
        const accounts = await window.ethereum.request({ method: 'eth_accounts' });
        if (accounts.length > 0) {
          await connectWallet();
        }
      } catch (error) {
      const err = error as Error;
      console.error('Error checking connection:', err);
      }
    }
  };

  const addXLayerNetwork = async () => {
    if (!window.ethereum) return false;
    
    try {
      await window.ethereum.request({
        method: 'wallet_addEthereumChain',
        params: [{
          chainId: `0x${XLAYER_NETWORK.chainId.toString(16)}`,
          chainName: XLAYER_NETWORK.chainName,
          nativeCurrency: XLAYER_NETWORK.nativeCurrency,
          rpcUrls: XLAYER_NETWORK.rpcUrls,
          blockExplorerUrls: XLAYER_NETWORK.blockExplorerUrls,
        }],
      });
      return true;
    } catch (error) {
      console.error('Failed to add X Layer network:', error);
      return false;
    }
  };

  const switchToXLayer = async () => {
    if (!window.ethereum) return false;
    try {
      await window.ethereum.request({
        method: 'wallet_switchEthereumChain',
        params: [{ chainId: `0x${XLAYER_NETWORK.chainId.toString(16)}` }],
      });
      return true;
    } catch (error: unknown) {
      // If network doesn't exist, add it
      const anyErr = error as { code?: number };
      if (anyErr?.code === 4902) {
        return await addXLayerNetwork();
      }
      const err = error as Error;
      console.error('Failed to switch to X Layer network:', err);
      return false;
    }
  };

  const connectWallet = async () => {
    if (!window.ethereum) {
      toast.error('Please install MetaMask or other compatible wallet');
      return false;
    }

    setIsLoading(true);
    
    try {
      // Request account access
      await window.ethereum.request({ method: 'eth_requestAccounts' });
      
      // Switch to X Layer network
      const networkSwitched = await switchToXLayer();
      if (!networkSwitched) {
        toast.error('Switch to X Layer network failed');
        setIsLoading(false);
        return false;
      }

      // Create provider and signer
      const web3Provider = new ethers.BrowserProvider(window.ethereum);
      const web3Signer = await web3Provider.getSigner();
      const userAddress = await web3Signer.getAddress();
      
      setProvider(web3Provider);
      setSigner(web3Signer);
      setAddress(userAddress);
      setIsConnected(true);
      
      // Get balance
      const userBalance = await web3Provider.getBalance(userAddress);
      setBalance(ethers.formatEther(userBalance));

      toast.success('Wallet connected successfully');
      return true;
    } catch (error) {
    const err = error as Error;
    console.error('Failed to connect wallet:', err);
    toast.error('Failed to connect wallet: ' + (err.message || 'Unknown error'));
      return false;
    } finally {
      setIsLoading(false);
    }
  };

  const disconnect = () => {
    setIsConnected(false);
    setAddress(null);
    setBalance('0');
    setProvider(null);
    setSigner(null);
    toast.success('Wallet disconnected');
  };

  const createTokenLaunch = useCallback(async (params: CreateXLayerTokenParams & { imageUrl?: string }) => {
    if (!signer || !isConnected) {
      throw new Error('Wallet not connected');
    }

    try {
      // First upload image if exists
      let imageUrl = params.imageUrl;
      if (params.imageFile && !imageUrl) {
        const uploadResult = await apiClient.uploadXLayerImage(params.imageFile);
        imageUrl = uploadResult.url;
      }

      const contract = new ethers.Contract(LAUNCHPAD_ADDRESS, LAUNCHPAD_ABI, signer);
      
      // Convert target amount to wei
      const targetAmountWei = ethers.parseEther(params.targetAmount);

      // Creation fee (hardcoded per contract assumption)
      const creationFee = ethers.parseEther('0.01');
      
      const tx = await contract.createTokenLaunch(
        params.name,
        params.symbol,
        params.description,
        params.website || '',
        params.telegram || '',
        params.twitter || '',
        targetAmountWei,
        { value: creationFee }
      );
      
      const receipt = await tx.wait();
      
      // Parse the TokenLaunchCreated event
      let launchId = 0;
      let tokenAddress = '';
      
      const event = receipt.logs.find((log: unknown) => {
        try {
            // eslint-disable-next-line @typescript-eslint/no-explicit-any
            const parsedLog = contract.interface.parseLog(log as any);
          return parsedLog?.name === 'TokenLaunchCreated';
        } catch {
          return false;
        }
      });
      
      if (event) {
        const parsedEvent = contract.interface.parseLog(event);
        launchId = parsedEvent?.args.launchId.toNumber();
        tokenAddress = parsedEvent?.args.tokenAddress;
      }

      // Save to database via API
      try {
        await apiClient.createXLayerToken({
          ...params,
          imageUrl
        });
      } catch (apiError) {
        console.error('Failed to save token to database:', apiError);
        // Continue even if API fails, as the contract transaction succeeded
      }
      
      return {
        success: true,
        launchId,
        tokenAddress,
        txHash: receipt.hash
      };
    } catch (error) {
      const err = error as Error;
      console.error('Token creation error:', err);
      return {
        success: false,
        error: err.message || '创建失败'
      };
    }
  }, [signer, isConnected]);

  // 买入（原 contribute -> buyTokens）
  const contribute = useCallback(async (params: XLayerTradeParams) => {
    if (!signer || !isConnected) {
      throw new Error('Wallet not connected');
    }

    try {
      const contract = new ethers.Contract(LAUNCHPAD_ADDRESS, LAUNCHPAD_ABI, signer);
      const ethAmount = ethers.parseEther(params.amount);

      const tx = await contract.buyTokens(params.launchId, { value: ethAmount });
      const receipt = await tx.wait();
      
      // Parse TokenPurchase event
      let tokensReceived = '0';
      let newPrice = '0';
      
      const event = receipt.logs.find((log: unknown) => {
        try {
            // eslint-disable-next-line @typescript-eslint/no-explicit-any
            const parsedLog = contract.interface.parseLog(log as any);
          return parsedLog?.name === 'TokenPurchase';
        } catch {
          return false;
        }
      });
      
      if (event) {
        const parsedEvent = contract.interface.parseLog(event);
        tokensReceived = ethers.formatEther(parsedEvent?.args.tokensReceived || '0');
        newPrice = ethers.formatEther(parsedEvent?.args.newPrice || '0');
      }

      // Update database via API
      try {
        await apiClient.contributeToXLayerToken(params.launchId.toString(), {
          contributor: address!,
          amount: params.amount,
          txHash: receipt.hash
        });
      } catch (apiError) {
        console.error('Failed to update contribution in database:', apiError);
        // Continue even if API fails, as the contract transaction succeeded
      }
      
      return {
        success: true,
        tokensReceived,
        ethSpent: params.amount,
        newPrice,
        txHash: receipt.hash
      };
    } catch (error) {
      const err = error as Error;
      console.error('Contribution error:', err);
      return {
        success: false,
        error: err.message || 'Purchase failed'
      };
    }
  }, [signer, isConnected, address]);

  const getTokenBalance = useCallback(async (tokenAddress: string, userAddress?: string) => {
    if (!provider) return '0';
    
    try {
      const contract = new ethers.Contract(tokenAddress, ERC20_ABI, provider);
      const balance = await contract.balanceOf(userAddress || address);
      return ethers.formatEther(balance);
    } catch (error) {
      console.error('Failed to get token balance:', error);
      return '0';
    }
  }, [provider, address]);

  const getLaunchInfo = useCallback(async (launchId: number) => {
    if (!provider) return null;
    
    try {
      const contract = new ethers.Contract(LAUNCHPAD_ADDRESS, LAUNCHPAD_ABI, provider);
      const info = await contract.getLaunchInfo(launchId);
      // info is a struct-like return; using array indices for ethers v6 compatibility
      return {
        tokenAddress: info.tokenAddress || info[0],
        creator: info.creator || info[1],
        name: info.name || info[2],
        symbol: info.symbol || info[3],
        description: info.description || info[4],
        targetAmount: ethers.formatEther(info.targetAmount || info[5]),
        raisedAmount: ethers.formatEther(info.raisedAmount || info[6]),
        deadline: Number(info.deadline || info[7]),
        launched: (info.launched ?? info[8]) as boolean,
        cancelled: (info.cancelled ?? info[9]) as boolean,
        tokenPrice: ethers.formatEther(info.currentPrice || info[10]), // alias
        currentPrice: ethers.formatEther(info.currentPrice || info[10]),
        tokensSold: ethers.formatEther(info.tokensSold || info[11]),
        totalSupplyForSale: ethers.formatEther(info.totalSupplyForSale || info[12])
  } as Record<string, unknown>;
    } catch (error) {
      console.error('Failed to get launch info:', error);
      return null;
    }
  }, [provider]);

  const getUserContribution = useCallback(async (launchId: number, userAddress?: string) => {
    if (!provider) return '0';
    
    try {
      const contract = new ethers.Contract(LAUNCHPAD_ADDRESS, LAUNCHPAD_ABI, provider);
      const contribution = await contract.getUserContribution(launchId, userAddress || address);
      return ethers.formatEther(contribution);
    } catch (error) {
      console.error('Failed to get user contribution:', error);
      return '0';
    }
  }, [provider, address]);

  return {
    isConnected,
    address,
    balance,
    provider,
    signer,
    isLoading,
    connect: connectWallet,
    disconnect,
    createTokenLaunch,
    contribute,
    getTokenBalance,
    getLaunchInfo,
    getUserContribution
  };
}